package com.ssky.bean.validator.proxy;

import com.ssky.bean.validator.core.IFieldValueGetter;
import com.ssky.bean.validator.core.VldConstants;
import com.ssky.bean.validator.util.DefinerUtils;
import javassist.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: ssky
 * @Date: 2021/07/20
 * @Description: 创建IFieldValueGetter的实现类
 */
public class FieldValueGetterCreator {
    public static final Logger L = LoggerFactory.getLogger(FieldValueGetterCreator.class);

    private static final String NLine = "\n";
    private static final String START_CLASS_NAME = "fvg$";
    private static final String ParamName = "local";
    private static final ClassPath classPath = new ClassClassPath(IFieldValueGetter.class);
    private static final String PackageStr = DynProxyImplPackagePosition.class.getPackage().getName();

    //<  class ,  <fieldName,IFullFieldValueGetter>   >
    private final Map<Class, Map<String, IFieldValueGetter>> classFieldgetterMap;


    public FieldValueGetterCreator() {
        classFieldgetterMap = new HashMap<>();
    }

    //   Object getV( Object local);
    public Map<Class, Map<String, IFieldValueGetter>> createAll(Class beanClass) throws Exception {
        Field[] fieldArray = beanClass.getDeclaredFields();
        if (fieldArray == null) {
            return null;
        }
        Map<String, IFieldValueGetter> beanValueGetterMap = new HashMap<>();
        for (Field ff : fieldArray) {
            if (Modifier.isStatic(ff.getModifiers())) {
                continue;
            }
            Class<?> ffClass = ff.getType();
            if (DefinerUtils.isCollection(ffClass)) {
                Type genericType = ff.getGenericType();
                if (genericType != null && genericType instanceof ParameterizedType) {
                    ParameterizedType pt = (ParameterizedType) genericType;
                    //得到泛型里的class类型对象
                    Class<?> accountPrincipalApproveClazz = (Class<?>) pt.getActualTypeArguments()[0];
                    ffClass = accountPrincipalApproveClazz; //找出集合中实际存放对象的类型
                } else {
                    continue;
                }
            }
            IFieldValueGetter getter = create(beanClass, ff);
            beanValueGetterMap.put(ff.getName(), getter);
            if (beanValueGetterMap.size() > 0) {
                this.classFieldgetterMap.put(beanClass, beanValueGetterMap);
            }

            if (DefinerUtils.isJavaClass(ffClass)) {
                continue;
            }

            Field[] tfArray = ffClass.getDeclaredFields();
            if (tfArray == null) continue;
            Map<String, IFieldValueGetter> fieldValueGetterMap = new HashMap<>();
            for (Field tf : tfArray) {
                if (Modifier.isStatic(tf.getModifiers())) {
                    continue;
                }
                IFieldValueGetter fieldGetter = create(ffClass, tf);
                fieldValueGetterMap.put(tf.getName(), fieldGetter);
            }
            if (fieldValueGetterMap.size() > 0) {
                this.classFieldgetterMap.put(ffClass, fieldValueGetterMap);
            }
        }
        return classFieldgetterMap;
    }

    //   Object getV( Object local);
    private IFieldValueGetter create(Class localClass, Field field) throws Exception {
        CtClass impCtClass = null;
        ClassPool pool = new ClassPool(true);
        pool.insertClassPath(classPath);
        CtClass ctInterface = pool.getCtClass(IFieldValueGetter.class.getName());
        impCtClass = pool.makeClass(PackageStr + "." + START_CLASS_NAME + field.getName() + VldConstants.Atomic_Long.getAndIncrement());
        impCtClass.addInterface(ctInterface);


        String methodStr = genMthodStr(localClass, field);
        CtMethod method = null;
        try {
            method = CtNewMethod.make(methodStr, impCtClass);
            impCtClass.addMethod(method);
        } catch (CannotCompileException e) {
            L.error("impCtClass.addMethod(method) occur exception ,methodStr --->" + methodStr, e);
            throw e;
        }

        Class<IFieldValueGetter> clazz = (Class<IFieldValueGetter>) impCtClass.toClass(ValidatorClassLoader.getCustomCLoader(FieldValueGetterCreator.class));
        // todo   清除javassist缓存
        IFieldValueGetter fieldValueGetter = clazz.newInstance();

        Class<?> fieldClass = field.getType();
        if (DefinerUtils.isCollection(fieldClass)) {
            Type genericType = field.getGenericType();
            if (genericType != null && genericType instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType) genericType;
                //得到泛型里的class类型对象
                Class<?> accountPrincipalApproveClazz = (Class<?>) pt.getActualTypeArguments()[0];
                fieldClass = accountPrincipalApproveClazz; //找出集合中实际存放对象的类型
            } else {
                return fieldValueGetter;
            }
        }

        if (DefinerUtils.isJavaClass(fieldClass)) {
            return fieldValueGetter;
        }
        //普通bean
        Field[] fieldArray = fieldClass.getDeclaredFields();
        if (fieldArray == null) {
            return fieldValueGetter;
        }

        Map<String, IFieldValueGetter> fieldValueGetterMap = new HashMap<>();
        for (Field nf : fieldArray) {
            if (Modifier.isStatic(nf.getModifiers())) {
                continue;
            }
            IFieldValueGetter getter = create(fieldClass, nf);
            fieldValueGetterMap.put(nf.getName(), getter);
        }
        if (fieldValueGetterMap.size() > 0) {
            this.classFieldgetterMap.put(fieldClass, fieldValueGetterMap);
        }
        return fieldValueGetter;
    }


    private String genMthodStr(Class preClass, Field field) {
        String methodStr = "public Object " + IFieldValueGetter.Method_Name + " ( Object " + ParamName + ")";

        StringBuilder body = new StringBuilder();
        body.append("{").append(NLine);
        body.append("if(" + ParamName + " != null){ ").append(NLine);
        Class<?> fieldType = field.getType();
        String vvGet = "((" + preClass.getName() + ")" + ParamName + ")." + DefinerUtils.genBeanGetMethodName(fieldType, field.getName()) + "()";
        if (DefinerUtils.isPrimitive(fieldType)) {
            body.append("   return  new ").append(DefinerUtils.getBoxType(fieldType)).append("(").append(vvGet).append(");").append(NLine);
        } else {
            body.append("   return ").append(vvGet).append(";").append(NLine);
        }

        body.append("}").append(NLine);
        body.append("return null;").append(NLine);
        body.append("}").append(NLine);
        methodStr = methodStr + body.toString();
        return methodStr;
    }
}
